package com.huajx.core.handler;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.alibaba.fastjson.JSON;
import com.huajx.core.Hboot;
import com.huajx.core.common.Columns;
import com.huajx.core.web.HbootWebConfig;
import com.huajx.util.StrUtil;
import com.huajx.util.convert.Convert;
import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.Template;
import org.beetl.core.resource.ClasspathResourceLoader;

import com.huajx.core.beetl.LongToDate;
import com.huajx.core.model.HttpRequestMessage;
import com.huajx.core.model.HttpResponseMessage;

public class Controller {
    protected String controllerKey;
    protected String viewPath;

//    private String urlPara;

    protected HttpRequestMessage request;
    protected HttpResponseMessage response;

    protected HbootWebConfig config = Hboot.config(HbootWebConfig.class);

    private final static String template = Hboot.config(HbootWebConfig.class).getTemplate();

    private boolean render;

    public String getPara() {
        String cxt = request.getContext();
        if(controllerKey.length() >= cxt.length() || StrUtil.equals(controllerKey, cxt)) {
            return null;
        }

        return StrUtil.subAfter(cxt, '/', true);
//        return urlPara;
    }

//    public void setUrlPara(String urlPara) {
//        this.urlPara = urlPara;
//    }

    /**
     * 是否渲染输出
     *
     * @return
     */
    public boolean isRender() {
        return render;
    }

    public final Map<String, Object> attrs = new ConcurrentHashMap<>();

    public void setAttr(String key, Object obj) {
        attrs.put(key, obj);
    }

    public void redirect(String url) {
        render = true;
        Invoke invoke = RouteHandler.getInvoke(url);
        invoke.setRequest(request);
        invoke.setResponse(response);
        try {
            invoke.invoke();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    public void render(String url) {
        render = true;
        response.setContentType(ConstantHttp.context_type_html);

        ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader();
        Configuration cfg;
        try {
            cfg = Configuration.defaultConfiguration();
            GroupTemplate gt = new GroupTemplate(resourceLoader, cfg);
            Template t = gt.getTemplate(template + url);
            gt.registerFunction("longToDate", new LongToDate());
            t.binding(attrs);
            String str = t.render();

            response.appendBody(str);
        } catch (IOException e) {
            e.printStackTrace();
            response.appendBody(e.getMessage());
        }
    }

//	public void renderLog(String filePath){
//		render = true;
//		response.setContentType(ConstantHttp.context_type_html);
//		
////		response.appendBody();//.loadFile("/page/" + context));
//	}

    public void render() {
        render(viewPath);
    }

    public void renderJson(Object obj) {
        render = true;
        response.setContentType(ConstantHttp.context_type_json);
        response.appendBody(JSON.toJSONBytes(obj));
    }

    public void renderJsonSuccess(){
        render = true;
        response.setContentType(ConstantHttp.context_type_json);
        response.appendBody("{\"success\":true}");
    }

    public void renderJsonFailure(){
        render = true;
        response.setContentType(ConstantHttp.context_type_json);
        response.appendBody("{\"success\":false}");
    }

    public String getControllerKey() {
        return controllerKey;
    }

    public void setControllerKey(String controllerKey) {
        this.controllerKey = controllerKey;
    }

    public String getViewPath() {
        return viewPath;
    }

    public void setViewPath(String viewPath) {
        this.viewPath = viewPath;
    }

    public HttpRequestMessage getRequest() {
        return request;
    }

    public void setRequest(HttpRequestMessage request) {
        this.request = request;
    }

    public HttpResponseMessage getResponse() {
        return response;
    }

    public void setResponse(HttpResponseMessage response) {
        this.response = response;
    }

    public String getPara(String para) {
        return request.getParameter(para);
    }

    public Integer getParaToInt(String para) {
        return Convert.toInt(getPara(para));
    }

    public Integer getParaToInt(String para, Integer defaultValue) {
        return Convert.toInt(getPara(para), defaultValue);
    }

    public Long getParaToLong(String para) {
        return Convert.toLong(getPara(para));
    }

    public Long getParaToLong(String para, Long defaultValue) {
        return Convert.toLong(getPara(para), defaultValue);
    }
    public <T> T getModel(Class<T> modelClass) {
        return getModel(modelClass, null, true);
    }

    public <T> T getModel(Class<T> modelClass, boolean skipConvertError) {
        return getModel(modelClass, null, skipConvertError);
    }

    public <T> T getModel(Class<T> modelClass, String beanName, boolean skipConvertError) {
        T bean = createInstance(modelClass);
        String modelNameAndDot = StrUtil.isNotBlank(beanName) ? beanName + "." : null;
        Map<String, String[]> parasMap = request.getParamenterMap();
        Method[] methods = modelClass.getMethods();
        for (Method method : methods) {
            String methodName = method.getName();
            if (!methodName.startsWith("set") || methodName.length() <= 3) {    // only setter method
                continue;
            }
            Class<?>[] types = method.getParameterTypes();
            if (types.length != 1) {                        // only one parameter
                continue;
            }

            String attrName = StrUtil.lowerFirst(methodName.substring(3));
            String paraName = modelNameAndDot != null ? modelNameAndDot + attrName : attrName;
            if (parasMap.containsKey("@"+paraName)) {
                try {
                    String paraValue = request.getParameter(paraName);
                    Object value = paraValue != null ? Convert.convert(types[0], paraValue) : null;
                    method.invoke(bean, value);
                } catch (Exception e) {
                    if (!skipConvertError) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return bean;
    }

    private static <T> T createInstance(Class<T> objClass) {
        try {
            return objClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
